#ifndef __PNCYCLEDBUFFER_HPP__
#define __PNCYCLEDBUFFER_HPP__

/**
*@file       pncycledbuffer.hpp
*@brief      cycled buffer
*
*@author     graywind
*@version    1.0.0.0
*@data       2011/11/24
*/ 

#include "interlock.h"
#include "boost/asio.hpp"

namespace pnNetlib
{
	template<typename T>
	class pnCycledBuffer
	{
	public:
		typedef T       data_type;

	private:
		T*      m_pArray;

		long    m_nCount;
		long    m_nReadIndex;
		long    m_nWriteIndex;
		long    m_nValidCount;
		long    m_nTailRemain;

	public:
		///@brief      constructor
		pnCycledBuffer(long count_of_elem);
		///@brief      destructor
		~pnCycledBuffer();

	public:
		///@brief   get total length of buffer(read-only)
		const long   total_length() const;

		///@brief   get valid read length
		const long   valid_length() const;

	public:
		///@brief    write to the buffer
		///param[in] pSrc            point to the first elem's address that writting in
		///param[in] count_of_elem   the count of elem that writting in
		///retval    bool
		bool   write_to_buffer(T* pSrc, long count_of_elem);

		///@brief   read from the buffer
		///param[in] pSrc            point to the first elem's address that filling in
		///param[in] count_of_elem   the count of elem that writting in
		///retval    bool
		bool   read_from_buffer(T*  pDes, long count_of_elem);

	public:///paral
		///@brief    get the write pointer
		///retval    mutable_buffers_1
		boost::asio::mutable_buffers_1    get_write_buffer(long count_of_elem);

		///@brief    commit write
		///retval    void
		void   commit_buffer(long count_of_elem);

		///@brief    get the read pointer
		///retval    mutable_buffers_1
		boost::asio::mutable_buffers_1    get_read_buffer(long count_of_elem);

		///@brief   cast the content
		///param[in]    count_of_elem      how many elem to pop
		///retval   void      point to the first elem's address of the poping array
		void   cast_buffer(long count_of_elem);
	};

	/*********************************************************************************************************************************************/
	template<typename T>
	pnCycledBuffer<T>::pnCycledBuffer(long count_of_elem):
	m_nCount(count_of_elem),
	m_nReadIndex(0),
	m_nWriteIndex(0),
	m_nValidCount(0),
	m_nTailRemain(0),
	m_pArray(new T[count_of_elem])
	{

	}

	/*********************************************************************************************************************************************/
	template<typename T>
	pnCycledBuffer<T>::~pnCycledBuffer()
	{
		delete [] m_pArray;
	}

	/*********************************************************************************************************************************************/
	template<typename T>
	inline
	const
	long
	pnCycledBuffer<T>::total_length() const
	{
		return m_nCount;
	}
	/*********************************************************************************************************************************************/
	template<typename T>
	inline
	const
	long
	pnCycledBuffer<T>::valid_length() const
	{
		long length;
		inter_lock::exchange(&length, m_nValidCount);
		return length;
	}

	/*********************************************************************************************************************************************/
	template<typename T>
	bool
	pnCycledBuffer<T>::write_to_buffer(T *pSrc, long count_of_elem)
	{
		boost::asio::mutable_buffers_1 buffer = get_write_buffer(count_of_elem);
		if(boost::asio::buffer_size(buffer) != 0)
		{
			memcpy(boost::asio::buffer_cast<char*>(buffer), pSrc, count_of_elem * sizeof(T));
			commit_buffer(count_of_elem);
			return true;
		}

		return false;
	}

	/*********************************************************************************************************************************************/
	template<typename T>
	bool
	pnCycledBuffer<T>::read_from_buffer(T *pDes, long count_of_elem)
	{
		boost::asio::mutable_buffers_1 buffer = get_read_buffer(count_of_elem);
		if(boost::asio::buffer_size(buffer) != 0)
		{
			memcpy(pDes, boost::asio::buffer_cast<char*>(buffer), count_of_elem * sizeof(T));
			cast_buffer(count_of_elem);
			return true;
		}

		return false;
	}

	/*********************************************************************************************************************************************/
	template<typename T>
	boost::asio::mutable_buffers_1
	pnCycledBuffer<T>::get_write_buffer(long count_of_elem)
	{
        long validlength = valid_length();
		if(validlength + count_of_elem + m_nTailRemain <= m_nCount)
		{
			if(m_nWriteIndex + count_of_elem <= m_nCount)
				return boost::asio::buffer(&m_pArray[m_nWriteIndex], count_of_elem * sizeof(T) );
			else
				return boost::asio::buffer(&m_pArray[0], count_of_elem * sizeof(T) );
		}
		else
			return boost::asio::mutable_buffers_1(boost::asio::mutable_buffer());
	}

	/*********************************************************************************************************************************************/
	template<typename T>
	void
	pnCycledBuffer<T>::commit_buffer(long count_of_elem)
	{
		if(m_nWriteIndex + count_of_elem <= m_nCount)
			m_nWriteIndex = (m_nWriteIndex + count_of_elem) % m_nCount;
		else
		{
			m_nTailRemain = m_nCount - m_nWriteIndex;
			m_nWriteIndex = count_of_elem;
		}

        inter_lock::add(&m_nValidCount, count_of_elem);
	}

	/*********************************************************************************************************************************************/
	template<typename T>
	boost::asio::mutable_buffers_1
	pnCycledBuffer<T>::get_read_buffer(long count_of_elem)
	{
        long validlength = valid_length();
		if(count_of_elem <= validlength)
		{
			if(m_nReadIndex + count_of_elem <= m_nCount)
				return boost::asio::buffer(&m_pArray[m_nReadIndex], count_of_elem * sizeof(T) );
			else
				return boost::asio::buffer(&m_pArray[0], count_of_elem * sizeof(T) );
		}
		else
			return boost::asio::mutable_buffers_1(boost::asio::mutable_buffer());
	}

	/*********************************************************************************************************************************************/
	template<typename T>
	void
	pnCycledBuffer<T>::cast_buffer(long count_of_elem)
	{
		if(m_nReadIndex + count_of_elem <= m_nCount)
			m_nReadIndex = (m_nReadIndex + count_of_elem) % m_nCount;
		else
			m_nReadIndex = count_of_elem;

        inter_lock::sub(&m_nValidCount, count_of_elem);
	}

	/*********************************************************************************************************************************************/

};

#endif
